package lesson6.example2;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadPoolMonitor extends ThreadPoolExecutor {
	private final Logger logger = LoggerFactory.getLogger(getClass());

	int ac = 0;

	private AtomicLong totalCostTime = new AtomicLong();
	private AtomicLong totalTasks = new AtomicLong();

	private String poolName;

	private long minCostTime;
	private long maxCostTime;

	private ThreadLocal<Long> startTime = new ThreadLocal<>();

	public ThreadPoolMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue, String poolName) {
		this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), poolName);
	}

	public ThreadPoolMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, String poolName) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
		this.poolName = poolName;
	}

	public static ExecutorService newFixedThreadPool(int nThreads, String poolName) {
		return new ThreadPoolMonitor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
				poolName);
	}

	public static ExecutorService newCachedThreadPool(String poolName) {
		return new ThreadPoolMonitor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), poolName);
	}

	public static ExecutorService newSingleThreadExecutor(String poolName) {
		return new ThreadPoolMonitor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), poolName);
	}

	@Override
	public void shutdown() {
		// 统计已执行任务、正在执行任务、未执行任务数量
		logger.info("{} Going to shutdown. Executed tasks: {}, Running tasks: {}, Pending tasks: {}", this.poolName,
				this.getCompletedTaskCount(), this.getActiveCount(), this.getQueue().size());
		super.shutdown();
	}

	@Override
	public List<Runnable> shutdownNow() {
		// 统计已执行任务、正在执行任务、未执行任务数量
		logger.info("{} Going to immediately shutdown. Executed tasks: {}, Running tasks: {}, Pending tasks: {}",
				this.poolName, this.getCompletedTaskCount(), this.getActiveCount(), this.getQueue().size());
		return super.shutdownNow();
	}

	@Override
	protected void beforeExecute(Thread t, Runnable r) {
		startTime.set(System.currentTimeMillis());
	}

	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		long costTime = System.currentTimeMillis() - startTime.get();
		startTime.remove(); // 删除，避免占用太多内存
		// 设置最大最小执行时间
		maxCostTime = maxCostTime > costTime ? maxCostTime : costTime;
		if (totalTasks.get() == 0) {
			minCostTime = costTime;
		}
		minCostTime = minCostTime < costTime ? minCostTime : costTime;
		totalCostTime.addAndGet(costTime);
		totalTasks.incrementAndGet();

		logger.info(
				"{}-pool-monitor: " + "Duration: {} ms, PoolSize: {}, CorePoolSize: {}, ActiveCount: {}, "
						+ "Completed: {}, Task: {}, Queue: {}, LargestPoolSize: {}, "
						+ "MaximumPoolSize: {},  KeepAliveTime: {}, isShutdown: {}, isTerminated: {}",
				this.poolName, costTime, this.getPoolSize(), this.getCorePoolSize(), super.getActiveCount(),
				this.getCompletedTaskCount(), this.getTaskCount(), this.getQueue().size(), this.getLargestPoolSize(),
				this.getMaximumPoolSize(), this.getKeepAliveTime(TimeUnit.MILLISECONDS), this.isShutdown(),
				this.isTerminated());
	}

	public int getAc() {
		return ac;
	}

	public float getAverageCostTime() {
		return totalCostTime.get() / totalTasks.get();
	}

	public long getMaxCostTime() {
		return maxCostTime;
	}

	public long getMinCostTime() {
		return minCostTime;
	}

	/**
	 * 生成线程池所用的线程，改写了线程池默认的线程工厂
	 */
	static class EventThreadFactory implements ThreadFactory {
		private static final AtomicInteger poolNumber = new AtomicInteger(1);
		private final ThreadGroup group;
		private final AtomicInteger threadNumber = new AtomicInteger(1);
		private final String namePrefix;

		EventThreadFactory(String poolName) {
			SecurityManager s = System.getSecurityManager();
			group = Objects.nonNull(s) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
			namePrefix = poolName + "-pool-" + poolNumber.getAndIncrement() + "-thread-";
		}

		@Override
		public Thread newThread(Runnable r) {
			Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
			if (t.isDaemon())
				t.setDaemon(false);
			if (t.getPriority() != Thread.NORM_PRIORITY)
				t.setPriority(Thread.NORM_PRIORITY);
			return t;
		}
	}
}
